package org.dei.perla.sys.llqruntime;

//TODO In questa classe gestire le get(0) in caso ci sia + di una query!!!

import java.util.*;
import org.dei.perla.parser.clauses.*;
import org.dei.perla.parser.datastructures.*;
import org.dei.perla.parser.expressions.*;
import org.dei.perla.parser.statements.*;
import org.dei.perla.parser.util.Duration;
import org.dei.perla.sys.llqruntime.datastructures.Buffer;
import org.dei.perla.sys.llqruntime.datastructures.FrequencyRecord;
import org.dei.perla.sys.llqruntime.datastructures.QueryRecordStructure;
import org.dei.perla.sys.llqruntime.datastructures.SamplingFrequenciesTable;


/**
 * Fornisce metodi per analizzare Query
 * @author Camplani, Fortunato, Marelli, Rota et al. 
 *
 */
public class QueryAnalyzer {
	
	/**
	 * Query nel formato in classi Java
	 */
	private org.dei.perla.parser.statements.Query pQuery;
	
	/**
	 * Costruttore 
	 * @param parQuery Query da analizzare
	 */
	public QueryAnalyzer(org.dei.perla.parser.statements.Query parQuery) {
			
		this.pQuery = parQuery;
	}
	
	/**
	 * Restituisce un buffer delle dimensioni adeguate
	 */
	public Buffer getSuitableBuffer() {
				
		int tSamples = ((UpToSamplesBased) pQuery.getLLStatements().get(0).getUpToClause()).getSamples();
		
		//Come fare? Per ora lo dimensione in base ai campioni di UPTO x SAMPLES
		
		return new Buffer(tSamples);
	}
	
	/**
	 * Restituisce gli ID dei device su cui effettuare le query di attuazione
	 * @return l'ArrayList contenente gli ID
	 */
	public ArrayList<ConstantID> getDeviceToSetID() {
		
		return this.pQuery.getSetStatements().get(0).getIDs();
	}
	
	/**
	 * Restituisce gli attributi da settare sui device
	 */
	public HashMap<String,Constant> getAttributeToSet() {
		
		return this.pQuery.getSetStatements().get(0).getAttributesToSet();
		
	}
	/**
	 * Metodo che restituisce la condizione Where
	 * @return il nodo che rappresenta la condizione
	 */
	public Node getWhereCondition() {
		
		return this.pQuery.getLLStatements().get(0).getSamplingClause().getWhereClause().getCondition();
		
	}
	
	/**
	 * Metodo che restituisce la struttura dati per decidere la frequenza di campionamento
	 * @return La struttura dati
	 */
	public SamplingFrequenciesTable getSamplingTable() {
		
		SamplingClause tClause = this.pQuery.getLLStatements().get(0).getSamplingClause();
		
		
		
		if (tClause instanceof SamplingTimeBased) {
			
			ArrayList<FrequencyRecord> tArrayList = new ArrayList<FrequencyRecord>();
			
			for (SamplingIfEvery tSampling : ((SamplingTimeBased) tClause).getPeriods()) {
				
				Duration tDuration = null;
				
				try {
					
					tDuration = new Duration(tSampling.getSamplingRate().getResult().getValueFloat(),tSampling.getTimeUnit());
				} catch (Exception e) {
					
					e.printStackTrace();
				}
				tArrayList.add(new FrequencyRecord(tSampling.getCondition(),tDuration));
				//tArrayList.add(new FrequencyRecord(tSampling.getCondition(),tNodeComparison,tDuration));	
								
			}
		return new SamplingFrequenciesTable(tArrayList,((SamplingTimeBased) tClause).getOperation());	
		
		}else return null;
		
	}
	
	/**
	 * Restituisce la struttura di un record del buffer interno
	 */		
	public QueryRecordStructure getLocalBufferStructure() {
				
		QueryRecordStructure tRecordStructure = new QueryRecordStructure();
		ExpressionAnalyzer tExpressionAnalyzer = new ExpressionAnalyzer();
		ArrayList<Node> tNodesArrayList = new ArrayList<Node>();
		SamplingClause tSamplingClause = pQuery.getLLStatements().get(0).getSamplingClause();
		
		for (int i=0;i<pQuery.getLLStatements().size();i++) {
		
			//recupero i nodi dalla clausola HAVING e WHERE...
		
			tNodesArrayList.add(pQuery.getLLStatements().get(i).getHavingClause().getCondition());
			tNodesArrayList.add(pQuery.getLLStatements().get(i).getSamplingClause().getWhereClause().getCondition());
		
			//...recupero i nodi dalla clausola SELECT...
		
			for (SelectField tSelectField : pQuery.getLLStatements().get(i).getSelectClause().getFields()) {
						
				tNodesArrayList.add(tSelectField.getExpression());		
			}
		
			//...recupero i nodi della SAMPLING (solo se questa è TimeBased)
		
			if (tSamplingClause instanceof SamplingTimeBased) {
			
				for (SamplingIfEvery tIfElement : ((SamplingTimeBased) tSamplingClause).getPeriods()) {
				
					tNodesArrayList.add(tIfElement.getCondition());
				}
			} 
		}
		//...FINE for, ora passo tutti i nodi raccolti all'ExpressionAnalyzer e restituisco la struttura.
		
		for (NodeLogicalObjectAttribute tNode : tExpressionAnalyzer.getFields(tNodesArrayList)) {
			
			//Manca il metodo per ottenere il tipo in NodeLogicalObjectAttribute
			tRecordStructure.addFieldStructure(tNode.toString(), tNode.getClass());	
		} 
		
		return tRecordStructure;		
	}
	
	/**
	 * Restituisce la struttura del buffer di uscita di una query
	 */
	public QueryRecordStructure getOutputBufferStructure() {
				
		StatementFieldList tFieldList = pQuery.getLLStatements().get(0).getFields();
		QueryRecordStructure tQueryRecordStructure = new QueryRecordStructure();
		
		for (Field tField : tFieldList) {
			
			tQueryRecordStructure.addFieldStructure(tField.getName(), tField.getType().getFieldTypeClass());			
		}
		
		return tQueryRecordStructure;
	}
}
